﻿import time
import datetime
import importlib
import importlib.util
import inspect
import threading
import traceback
import multiprocessing
from billiard.pool import Pool
import concurrent.futures
from concurrent.futures import ThreadPoolExecutor
import requestNovelsLib
import sys
import os
import typing

## 线程数量
__workerThreads = 8
### 切割字符串
__cuttingStr = "< ========= >"
## 总进程数量
__PoolCount = 4


def toSplitShowWordFont(tem: str, splitKey: str) -> list[str]:
	"""
	从 tem 切割字符，切割关键字为 spaceWork 
	:param tem: 需要被切割的字符
	:param splitKey: 切割关键字
	:return: 切割完成的列表
	"""
	result: list[str] = []
	temSpaceArray = tem.split(splitKey)
	for spaceWork in temSpaceArray:
		if spaceWork == "":
			continue
		result.append(spaceWork)
	return result


def toShowWordFont(tem: str) -> list[str]:
	result: list[str] = []
	if tem is None:
		return None
	tem = tem.rstrip()
	if tem == "":
		return None
	
	spaceArray = toSplitShowWordFont(tem, " ")
	if spaceArray is not None:
		for itemSpace in spaceArray:
			tabArray = toSplitShowWordFont(itemSpace, "\t")
			if tabArray is not None:
				for itemTab in tabArray:
					enterArray = toSplitShowWordFont(itemTab, "\n")
					if enterArray is not None:
						for itemEnter in enterArray:
							result.append(itemEnter)
	return result


def initIgnore(argvMaps: typing.Dict[str, typing.List[str]]) -> typing.Union[None, typing.List[str]]:
	"""
	初始化一个忽略参数
	"""
	igList0 = argvMaps.get("i")
	igList1 = argvMaps.get("ig")
	igList2 = argvMaps.get("ignore")
	resultBuffList: typing.List[str] = []
	if igList1 is not None:
		for arg in igList1:
			spriteName = requestNovelsLib.getBaseFileName(arg, checkFileSuffix=".py").upper()
			resultBuffList.append(spriteName)
	if igList2 is not None:
		for arg in igList2:
			spriteName = requestNovelsLib.getBaseFileName(arg, checkFileSuffix=".py").upper()
			resultBuffList.append(spriteName)
	if igList0 is not None:
		for arg in igList0:
			spriteName = requestNovelsLib.getBaseFileName(arg, checkFileSuffix=".py").upper()
			resultBuffList.append(spriteName)
	if len(resultBuffList) == 0:
		return None
	return resultBuffList


def getTargetAbsFilePath(rootPath: str, fileSuffix: str = None) -> list[str]:
	"""
	获取目录下的所有指定的后缀名，返回的是绝对路径
	:param rootPath: 根目录
	:param fileSuffix: 后缀
	:return: 匹配的后缀文件名
	"""
	
	result: list[str] = []
	if not os.path.exists(rootPath):
		return None
	if fileSuffix is not None and len(fileSuffix) == 0:
		fileSuffix = None
	if fileSuffix is not None and fileSuffix[0] != '.':
		fileSuffix = f".{fileSuffix}"
	buffSubResult: list[str] = None
	buffPath: str = None
	fileOrDirs = requestNovelsLib.getTargetInDirAbsFilePath(rootPath)
	for fileOrDir in fileOrDirs:
		if os.path.isfile(fileOrDir):
			if fileSuffix is not None:
				if fileOrDir.endswith(fileSuffix) == 1:
					result.append(fileOrDir)
			else:
				result.append(fileOrDir)
	return result


def getRunPyScripts(runScriptNameArgs: list[str], ignorePyNames: list[str]) -> list[str]:
	"""
	运行指定的模块
	:param runScriptNameArgs: 是否运行模块， 非空表示运行，若需要指定运行，则需要输入匹配的模块名称
	:param ignorePyNames: 忽略的模块名称
	"""
	runScriptFilesPath = []
	ignore = False
	buff = []
	buffRunScriptFilesPath = requestNovelsLib.getTargetInDirAbsFilePath(requestNovelsLib.getCallAbsDirPath())
	## 获取所有 .py 结尾
	for fileItem in buffRunScriptFilesPath:
		if fileItem.endswith(".py"):
			buff.append(fileItem)
	buffRunScriptFilesPath = buff
	if runScriptNameArgs is not None:
		## 获取需要运行的
		if len(runScriptNameArgs) != 0:
			for pythonName in runScriptNameArgs:
				basePythonName = requestNovelsLib.getBaseFileName(pythonName).upper()
				for fullPath in buff:
					baseName = requestNovelsLib.getBaseFileName(fullPath).upper()
					if baseName == basePythonName:
						runScriptFilesPath.append(fullPath)
		else:
			runScriptFilesPath = buffRunScriptFilesPath
		## 获取所有忽略的
		if ignorePyNames is not None:
			buff = []
			buffRunScriptFilesPath = []
			for image in ignorePyNames:
				buff.append(requestNovelsLib.getBaseFileName(image).upper())
			ignore = True
			for fileAllName in runScriptFilesPath:
				ignore = False
				pyBaseName = requestNovelsLib.getBaseFileName(fileAllName).upper()
				for compIgnore in buff:
					if compIgnore == pyBaseName:
						ignore = True
						break
				if not ignore:
					buffRunScriptFilesPath.append(fileAllName)
			runScriptFilesPath.clear()
			for pyFile in buffRunScriptFilesPath:
				name = requestNovelsLib.getBaseFileName(pyFile)
				path = requestNovelsLib.getTargetAbsSavePath(pyFile)
				improtMode = importlib.util.find_spec(name, path)
				if improtMode is not None:
					modeResult = improtMode.loader.load_module()
					for getDir in dir(modeResult):
						if getDir == "modeRequestGetUrl":
							runScriptFilesPath.append(pyFile)
							break
			return runScriptFilesPath
	buffRunScriptFilesPath = []
	for pyFile in runScriptFilesPath:
		name = requestNovelsLib.getBaseFileName(pyFile)
		path = requestNovelsLib.getTargetAbsSavePath(pyFile)
		improtMode = importlib.util.find_spec(name, path)
		if improtMode is not None:
			modeResult = improtMode.loader.load_module()
			for getDir in dir(modeResult):
				if getDir == "modeRequestGetUrl":
					buffRunScriptFilesPath.append(pyFile)
					break
	return buffRunScriptFilesPath


class Pari:
	def __init__(self, key, value):
		self.key = key
		self.value = value


def runTargetScriptsModeAtThread(filePath, lock):
	modeName = requestNovelsLib.getBaseFileName(filePath)
	print(f"=================>       运行      {modeName} <=========")
	model = requestNovelsLib.getPathPythonModels(filePath)
	if model is None:
		return None
	try:
		model.modeRequestGetUrl(lock)
	except:
		traceback.print_exc()
	print(f"=================>     执行完毕      {modeName} <=========")
	return model


def getFileFindKeyWords(filePath: str, pythonScriptPath: str, cutterStr: str) -> list[str]:
	"""
	查找文件关键字
	@param filePath: 文件路径
	@param pythonScriptPath: 脚本路径
	@param cutterStr: 分割字符串
	@return: 文件中的关键字列表
	"""
	## 获取文件内容
	try:
		print(f"获取 {filePath} 路径关键字")
		if os.path.isfile(filePath):
			absFilePath = requestNovelsLib.getTargetInDirAbsFilePath(filePath)
			if len(absFilePath) == 0:
				return []
			conent = requestNovelsLib.readFile(absFilePath[0])
			return conent.upper().replace("\ufeff", "").split(cutterStr)
		filesContenMap = requestNovelsLib.readDirFiles(filePath)
		resultList: list[str] = []
		
		for fileFullPathName, fileConten in filesContenMap.items():
			replactConten = fileConten.upper().replace("\ufeff", "").split(cutterStr)
			for key in replactConten:
				newKey = key.strip()
				if len(newKey) != 0:
					resultList.append(newKey)
		return resultList
	except:
		requestNovelsLib.__stdError(requestNovelsLib.getSysExcInfoErrorMsg())
	return []


def initFindKeys(kOption: list[str], fOption: list[str], fOptionFileCutterStr: str = None) -> typing.Dict[str, list[str]]:
	"""
	获取所有关键字,他不允许使用 py 后缀来指定关键字文件
	@param kOption: k 选项关键字，由命令行提供
	@param fOption: f 选项文件/文件夹，由命令行提供，该函数会读取所有匹配的文件或文件夹
	@param fOptionFileCutterStr: 文件内容的切分符
	@return: 路径映射到关键字的配对，可以参考路径来实现相对存放，-k 选项发挥为 “”
	"""
	## 处理空的非法分隔符
	if fOptionFileCutterStr is None or len(fOptionFileCutterStr) == 0:
		fOptionFileCutterStr = '\n'
	else:
		fOptionFileCutterStr = fOptionFileCutterStr.replace("\\t", '\t').replace("\\n", '\n')
	result: typing.Dict[str, list[str]] = {}
	currentPath = requestNovelsLib.getCallAbsDirPath()
	global __PoolCount
	with multiprocessing.Pool(requestNovelsLib.getWorkerMultiProcessCount()) as processPool:
		
		## 存储进程返回
		processRunTimeObjMap: typing.Dict[str, multiprocessing.pool.ApplyResult] = {}
		## 遍历所有文件
		for argFilePath in fOption:
			if not argFilePath.endswith("py"):
				try:
					processRunTimeObjMap[argFilePath] = processPool.apply_async(getFileFindKeyWords, (argFilePath, currentPath, fOptionFileCutterStr,))  ## args = [argFilePath, currentPath, fOptionFileCutterStr, ]  ## processRunTimeObjMap[argFilePath] = processPool.apply_async(lambda p: getFileFindKeyWords(*p), args)
				except:
					msg = requestNovelsLib.getSysExcInfoErrorMsg()
					requestNovelsLib.__stdError(msg)
					requestNovelsLib.writeLogFile(msg, "initFindKeys error_find_option")
		## 配置 -k 选项
		if kOption is not None and len(kOption):
			kOptionFileName = requestNovelsLib.getPathName(__file__)
			buffHumanNameList = []
			for keyWord in kOption:
				for humanNames in requestNovelsLib.getHumanNameList(keyWord):
					buffHumanNameList.append(humanNames)
			## 缩短字符串
			buffHumanNameList = requestNovelsLib.strListAtStrip(buffHumanNameList, 0)
			buffHumanNameList = requestNovelsLib.strListToChange(buffHumanNameList, True)
			buffHumanNameList = requestNovelsLib.strListreduce(buffHumanNameList)
			result[kOptionFileName] = requestNovelsLib.removeRepeateUnity(buffHumanNameList)
		processPool.close()
		processPool.join()
		resulitItems = processRunTimeObjMap.items()
		buffHumanNameList: list[str] = None
		for filePath, processPoolResult in resulitItems:
			buffHumanNameList = processPoolResult.get()
			if buffHumanNameList is None or len(buffHumanNameList) == 0:
				continue
			buffHumanNameList = requestNovelsLib.strListAtStrip(buffHumanNameList, 0)
			buffHumanNameList = requestNovelsLib.strListToChange(buffHumanNameList, True)
			buffHumanNameList = requestNovelsLib.strListreduce(buffHumanNameList)
			arrayList = requestNovelsLib.removeRepeateUnity(buffHumanNameList)
			if len(arrayList) != 0:
				result[filePath] = arrayList
	return result


def findNovelExisKey(findNovelInfo: requestNovelsLib.NovelInfo = None, keys: list[str] = None) -> str:
	"""
	判断是否存在关键字
	@param findNovelInfo: 判断对象
	@param keys: 关键字列表
	@return: 存在返回 关键字，否则返回 None
	"""
	upName = findNovelInfo.novelName.upper()
	upInfo = findNovelInfo.info.upper()
	upLastItem = findNovelInfo.lastItem.upper()
	upAuthor = findNovelInfo.author.upper()
	for key in keys:
		if upName.find(key) != -1 or upInfo.find(key) != -1 or upLastItem.find(key) != -1 or upAuthor.find(key) != -1:
			if len(findNovelInfo.additionalField) > 0:
				findNovelInfo.additionalField = f"{findNovelInfo.additionalField}\n\t关键字: {key}"
			else:
				findNovelInfo.additionalField = f"关键字: {key}"
			return key
	return None


def fromDbGetNovelInfo(dbPath: str, findObjs: list[requestNovelsLib.NovelInfo], iniFilePath, keys, runScrictPath, targetTopDirPath, oututMakeCode) -> dict[str, Pari]:
	"""
	查找匹配的小说内容
	@param dbPath: db 路径
	@param findObjs: 所有的小说信息
	@param iniFilePath: 配置文件路径
	@param keys: 查找的关键字
	@param runScrictPath: 脚本运行的路径
	@param targetTopDirPath: 顶部文件夹(带路径)
	@param oututMakeCode: 输出编码
	@return: 已经匹配好的对象, [路径，[关键字，小说对象]]
	"""
	if iniFilePath == "":
		return {}
	iniFileBaseName = requestNovelsLib.getPathName(iniFilePath)
	dbBaseName = requestNovelsLib.getBaseFileName(requestNovelsLib.getPathName(dbPath))
	targetDir = f"{runScrictPath}{os.sep}{targetTopDirPath}{os.sep}{iniFileBaseName}{os.sep}{dbBaseName}"
	targetFilePath = ""
	## 删除已经存在的文件
	# if os.path.exists(targetDir):
	# 	requestNovelsLib.removePath(targetDir)
	## 返回列表
	resultDict: dict[str, Pari] = {}
	## 遍历所有对象
	ingKey: str = ""
	## 过滤配置文件
	writeListNovelName: list[str] = []
	try:
		noveName = ""
		buffList: list[requestNovelsLib.NovelInfo] = []
		for findNovelInfo in findObjs:
			exisKeyWork = findNovelExisKey(findNovelInfo, keys)
			if exisKeyWork:
				targetFilePath = f"{targetDir}{os.sep}{findNovelInfo.novelTypeName}.txt"
				resultPari = resultDict.get(targetFilePath)
				if resultPari is None:
					resultPari = Pari(keys, [])
					resultDict[targetFilePath] = resultPari
				resultPari.value.append(findNovelInfo)
				buffList.append(findNovelInfo)
		if len(buffList) > 0:
			writeFilePath = f"{targetDir}/all.txt"
			call_abs_dir_path = requestNovelsLib.getCallAbsDirPath()
			infoFilePath = f"{call_abs_dir_path}/out/ini/ingFindInDBFiles/{dbBaseName}_{oututMakeCode}.ini"
			requestNovelsLib.__novelInfoExAtWriteFile(buffList, writeFilePath, infoFilePath)
	except:
		msg = f"查找异常({dbPath})\n {requestNovelsLib.getSysExcInfoErrorMsg()}"
		requestNovelsLib.__stdError(msg)
		requestNovelsLib.writeLogFile(msg, httpUrl="find novel error 1")
	try:
		for file, pair in resultDict.items():
			pair.value = requestNovelsLib.novelInfoTypeSort(requestNovelsLib.removeNovelsRepeatAtUrl(pair.value))
	except:
		msg = f"查找异常({dbPath})\n {requestNovelsLib.getSysExcInfoErrorMsg()}"
		requestNovelsLib.__stdError(msg)
		requestNovelsLib.writeLogFile(msg, httpUrl="find novel error 2")
	return resultDict


def getFindKeyInfo(keyFindMap: dict[str, list[str]], dbFilePaths: list[str], currentTimeStr: str, ispsDict: dict[int, list[str]] = None, hspsDict: dict[int, list[str]] = None) -> dict[str, list[requestNovelsLib.NovelInfo]]:
	"""
	获取所有匹配的小说
	@param keyFindMap: 路径与查找关键字的匹配
	@param dbFilePaths: 请求的 db 文件路径，包含所有的 db
	@param currentTimeStr: 当前时间的字符串格式
	@param ispsDict : 对等匹配路径
	@param hspsDict : 存在匹配路径
	@return: 已经匹配好的小说列表
	"""
	dbFilePaths = requestNovelsLib.removeRepeateUnity(dbFilePaths)
	if len(dbFilePaths) == 0:
		return {}
	poolResultObjList = []
	absDirPath = requestNovelsLib.getCallAbsDirPath()
	keyMap: dict[str, list[multiprocessing.pool.ApplyResult]] = {}
	global __PoolCount
	try:
		dbResultMap: dict[str, list[requestNovelsLib.NovelInfo]] = readDBNovels(__PoolCount, dbFilePaths, hspsDict, ispsDict)
		result = novelsWriteFile(__PoolCount, absDirPath, dbResultMap, keyFindMap)
		return result
	except:
		msg = requestNovelsLib.getSysExcInfoErrorMsg()
		requestNovelsLib.__stdError(msg)
		requestNovelsLib.writeLogFile(msg, "getFindKeyInfo call error")
	return {}


def novelsWriteFile(poolCount, absDirPath, dbResultMap, keyFindMap):
	result: dict[str, list[requestNovelsLib.NovelInfo]] = {}
	keyMap: list[str, list[concurrent.futures._base.Future]] = {}
	processResult: concurrent.futures._base.Future
	map_items = dbResultMap.items()
	with ThreadPoolExecutor(requestNovelsLib.getWorkerThreadNumber()) as pools:
		allWriteNovelEx: list[requestNovelsLib.NovelInfoEx] = []
		for dbPath, novels in map_items:
			oututMakeCode = 0
			dbFileCount = 0
			for filePath, initKeys in keyFindMap.items():
				try:
					dbFileCount += 1
					iniFileBaseName = requestNovelsLib.getPathName(filePath)
					dbBaseName = requestNovelsLib.getBaseFileName(requestNovelsLib.getPathName(dbPath))
					print(f"在《{dbBaseName}》中匹配 -《{iniFileBaseName}》- 文件保留的关键字 => 配置文件计数 : {dbFileCount}")
					poolResultObjList = keyMap.get(filePath)
					if poolResultObjList is None:
						poolResultObjList = []
						keyMap[filePath] = poolResultObjList
					topPath = "out/find"
					args = (dbPath, novels, filePath, initKeys, absDirPath, topPath, oututMakeCode)
					asy = pools.submit(fromDbGetNovelInfo, *args)
					oututMakeCode += 1
					poolResultObjList.append(asy)
				except:
					requestNovelsLib.__stdError(requestNovelsLib.getSysExcInfoErrorMsg())
			print(f"\n结束 ===\t{dbPath}")
		try:
			processResultDict: dict[str, Pari] = []
			writeListEx: list[requestNovelsLib.NovelInfoEx] = []
			for keyFilePath, poolResultObjList in keyMap.items():
				writeListEx = []
				for processResult in poolResultObjList:
					processResultDict = processResult.result()
					for writePathFileName, resultNovelsPair in processResultDict.items():
						exisNovelsPair = result.get(writePathFileName)
						for novel in resultNovelsPair.value:
							requestNovelsLib.novelInfoExAppendObj(writeListEx, novel)
							requestNovelsLib.novelInfoExAppendObj(allWriteNovelEx, novel)
						if exisNovelsPair is None:
							result[writePathFileName] = resultNovelsPair
						else:
							for saveNovel in resultNovelsPair.value:
								exisNovelsPair.value.append(saveNovel)
				try:
					infoFilePath = requestNovelsLib.getPathName(keyFilePath)
					writeFilePath = f"{absDirPath}/out/find/{infoFilePath}/all.txt"
					infoFilePath = f"{absDirPath}/out/ini/ingFindInDBFiles/{infoFilePath}.all.find.txt"
					requestNovelsLib.novelInfoExWriteFile(writeListEx, writeFilePath, infoFilePath)
				except:
					msg = requestNovelsLib.getSysExcInfoErrorMsg()
					requestNovelsLib.__stdError(msg)
					requestNovelsLib.writeLogFile(msg, "novelsWriteFile at write writeListEx error")
		except:
			msg = requestNovelsLib.getSysExcInfoErrorMsg()
			requestNovelsLib.__stdError(msg)
			requestNovelsLib.writeLogFile(msg, "novelsWriteFile call error")
		try:
			writeFilePath = f"{absDirPath}/out/find/all.txt"
			infoFilePath = f"{absDirPath}/out/ini/ingFindInDBFiles/all.find.txt"
			requestNovelsLib.novelInfoExWriteFile(allWriteNovelEx, writeFilePath, infoFilePath)
		except:
			msg = requestNovelsLib.getSysExcInfoErrorMsg()
			requestNovelsLib.__stdError(msg)
			requestNovelsLib.writeLogFile(msg, "novelsWriteFile at write all writeListEx error")
	return result


def readDBNovels(poolCount, dbFilePaths, hspsDict, ispsDict) -> dict[str, list[requestNovelsLib.NovelInfo]]:
	result: dict[str, list[requestNovelsLib.NovelInfo]] = {}
	dbResult: dict[str, concurrent.futures._base.Future] = {}
	## pools: multiprocessing.pool.Pool
	with ThreadPoolExecutor(requestNovelsLib.getWorkerMultiProcessCount()) as pools:
		for dbPath in dbFilePaths:
			dbResult[dbPath] = pools.submit(requestNovelsLib.outputSqlite3, *(dbPath,))
		subPools: multiprocessing.pool.Pool
		for dbPath, proResult in dbResult.items():
			proResultList: list[requestNovelsLib.NovelInfo] = proResult.result()
			if proResultList:
				proResultList = requestNovelsLib.dictFilterNovels(hspsDict, ispsDict, proResultList)
				if len(proResultList) > 0:
					result[dbPath] = proResultList
	return result


def writeFindResultNovels(wirteDict: dict[str, Pari] = None):
	"""
	把查找到的小说写入文件
	@param wirteDict: 内容映射，从文件到小说内容的映射
	"""
	if wirteDict is None:
		return
	allLen = 0
	lens = 0
	writeFileCount = 0
	keyCount = 0
	removeDirList = []
	
	for file, pair in wirteDict.items():
		## 是否删除文件夹内的所有内容
		fileDirPath = requestNovelsLib.getTargetAbsSavePath(file)
		fileBaseName = os.path.basename(fileDirPath)
		fileDirPath = fileDirPath[0: len(fileDirPath) - len(fileBaseName)]
		lens = 0
		for dirPath in removeDirList:
			if dirPath == fileDirPath:
				lens = 1
				break
		if lens == 0:
			removeFileList = requestNovelsLib.removePath(fileDirPath, ingBaseNames=["all.txt"])
			print(f"\n删除目录 {fileDirPath} (文件夹与文件)数量 : {len(removeFileList)}")
			removeDirList.append(fileDirPath)
		lens = len(pair.value)
		keyCount = len(pair.key)
		allLen += lens
		try:
			requestNovelsLib.writeFile(f"{requestNovelsLib.novelListToStr(pair.value)}\nkey = {', '.join(pair.key)}\n关键字 : {keyCount}\n已经查找到小说数量 : {lens}\n", file, "w")
			writeFileCount += 1
			print(f"文件=> {file} 写入 {lens} 个小说,关键字为 {keyCount} 个, 第 {writeFileCount} 个文件")
		except:
			msg = requestNovelsLib.getSysExcInfoErrorMsg()
			requestNovelsLib.__stdError(msg)
			requestNovelsLib.writeLogFile(msg, "write file db error(find write file)")
	
	print(f"共存在 => {allLen}, 写入文件数量 : {writeFileCount}")


def expireFileTimeFile(nowTime: datetime.datetime, checkPtah: str = "out/find", seconds=0, comFunction=None):
	## 删除过期文件
	dirFiles = requestNovelsLib.getPathFilesNames("out/find")
	expireFile: list[str] = []
	if comFunction is None:
		comFunction = lambda left, ritht: left < ritht
	for file in dirFiles:
		try:
			changeTime = datetime.datetime.fromtimestamp(os.path.getmtime(file))
			comResult = changeTime - nowTime
			if comFunction(comResult.total_seconds(), seconds):
				expireFile.append(file)
		except:
			msg = requestNovelsLib.getSysExcInfoErrorMsg()
			requestNovelsLib.__stdError(msg)
			requestNovelsLib.writeLogFile(msg, "find option error (writeFindResultNovels)")
	return expireFile


def removeEmptyDirs(directory):
	"""
	删除空目录
	@param directory:
	@type directory:
	@return:
	@rtype:
	"""
	for root, dirs, files in os.walk(directory, topdown=False):
		for foreachDir in dirs:
			path = os.path.join(root, foreachDir)
			if not os.listdir(path):  # 检查目录是否为空
				os.rmdir(path)  # 删除空目录


if __name__ == '__main__':
	print(f"正在从 {requestNovelsLib.currentCallThisFunctionFileName()} : {requestNovelsLib.currentCallThisFunctionFileLine()} 行 开始执行代码")
	
	## 初始化类库
	requestNovelsLib.initLib()
	
	## 当前时间的字符串
	datetime_now = datetime.datetime.now()
	currentTimeStr = datetime_now.strftime(f'%d_%H_%M_%S')
	
	## 查找配置文件 -f
	runFindCmdKeyFileList = []
	## 查找关键字 -k
	## 以 空格 为分隔符的关键字
	spaceSplitKeyWork = ""
	## 以 元素 为单位的关键字
	runFindCmdKeyList = []
	try:
		if spaceSplitKeyWork is not None:
			spaceSplitKeyWork = spaceSplitKeyWork.strip()
			if len(spaceSplitKeyWork) > 0:
				print(f"spaceSplitKeyWork = {spaceSplitKeyWork}")
				showWordArray = toShowWordFont(spaceSplitKeyWork)
				if showWordArray is not None:
					for x in showWordArray:
						runFindCmdKeyList.append(x)
				## 去掉重复
				runFindCmdKeyList = requestNovelsLib.removeRepeateUnity(runFindCmdKeyList)
	except:
		traceback.print_exc()
	
	## 填充脚本目录到查找列表
	argvDict = {'f': runFindCmdKeyFileList, 'k': runFindCmdKeyList}
	## 初始化查找信息
	paramArgs = requestNovelsLib.initParamArgs(argvDict)
	## 初始化忽略参数
	ignorePyNames = initIgnore(argvDict)
	## 存在 get 参数则开始运行爬虫
	getArgs = argvDict.get("get")
	## 删除重复
	ignorePyNames = requestNovelsLib.removeRepeateUnity(ignorePyNames, 0, 0)
	if getArgs is not None:
		## 脚本代理运行
		runScritpList = getRunPyScripts(getArgs, ignorePyNames)
		if len(runScritpList) > 0:
			writeInfo: str = f"{datetime.datetime.now().strftime('%Y年%m月%d日 %H 时 %M 分 %S 秒 %f 毫秒')}\n"
			with ThreadPoolExecutor(max_workers=__workerThreads) as pool:
				requestNovelsLib.writeFile(writeInfo, f"out/ini/runtimeInfo/runtimeInfo.txt")
				threadList: list[concurrent.futures._base.Future] = []
				removeLogFiles = requestNovelsLib.removeLogFiles()
				manager = requestNovelsLib.getLibManager()
				for filePaths in runScritpList:
					writeInfo = f"{writeInfo}运行 : {requestNovelsLib.getBaseFileName(filePaths)}\n\t{filePaths}\n"
					try:
						multiprocessingLock = manager.Lock()
						submit = pool.submit(lambda p: runTargetScriptsModeAtThread(*p), [filePaths, multiprocessingLock])
						requestNovelsLib.writeFile(writeInfo, f"out/ini/runtimeInfo/runtimeInfo.txt")
						threadList.append(submit)
					except:
						traceback.print_exc()
				pool.shutdown(wait=True)
				writeInfo = f"{writeInfo}\n============\n"
				for modeResult in threadList:
					model = modeResult.result()
					if model is None:
						continue
					urlNameFormat = '{0: >20}'.format(f'https://{model.getRootUrl()}')
					timeFormat = '{0: <20}'.format(f'{model.getRunTime()}')
					runEndTimeInfo = f"====> {urlNameFormat} 执行时间 {timeFormat}"
					writeInfo = f"{writeInfo}{runEndTimeInfo}\n"
					requestNovelsLib.writeFile(writeInfo, f"out/ini/runtimeInfo/runtimeInfo.txt")
					print(runEndTimeInfo)
				requestNovelsLib.writeFile(writeInfo, f"out/ini/runtimeInfo/runtimeInfo.txt")
			print(f"=================>       所有脚本运行完毕      <=========")
		else:
			print("没有找到( 需要运行/可用 )的脚本，请检查参数")
	else:
		print("\n====\n\t没有发现运行的脚本\n====\n")
	## 是否激活删除选项
	getArgs = argvDict.get("rp")
	if getArgs is not None:

		## 匹配时间
		arrayLen = len(getArgs)
		day = -1
		month = 0
		year = 0
		if arrayLen > 0:
			arrayIndex = 0
			while arrayIndex < arrayLen:
				try:
					if getArgs[arrayIndex] == "day":
						day = int(getArgs[arrayIndex + 1])
						arrayIndex = arrayIndex + 1
					elif getArgs[arrayIndex] == "month":
						month = int(getArgs[arrayIndex + 1])
						arrayIndex = arrayIndex + 1
					elif getArgs[arrayIndex] == "year":
						year = int(getArgs[arrayIndex + 1])
						arrayIndex = arrayIndex + 1
				finally:
					arrayIndex = arrayIndex + 1
			if day == -1:
				arrayIndex = 0
				while arrayIndex < arrayLen:
					try:
						day = int(getArgs[arrayIndex])
						break
					finally:
						arrayIndex = arrayIndex + 1
		if day == -1:
			## 如果无法找到日期，则使用默认
			day = 2
		print(f"\n====\n\t移除过期小说信息\t{month}月{day}日\n====\n")
		## 删除过期信息
		requestNovelsLib.removeRepeateSqlite3DB(f"{requestNovelsLib.getCallAbsDirPath()}{os.sep}/out/db", day=day, month=month, year=year)
	else:
		print("\n====\n\t没有发现 -rp 选项\n====\n")
	
	ispsDict = requestNovelsLib.readsPathsIniFileStrToLenStr(argvDict.get("isp"))
	hspsDict = requestNovelsLib.readsPathsIniFileStrToLenStr(argvDict.get("hsp"))
	## 是否解压到txt 文件
	getArgs = argvDict.get("db")
	if getArgs is not None:
		print("\n====\n\t小说信息写入 txt 文件\n====\n")
		currentPath = requestNovelsLib.getCallAbsDirPath() + os.sep
		## 解压文件
		dbPath = f"{currentPath}out{os.sep}db{os.sep}"
		targetPath = f"{currentPath}out{os.sep}txt{os.sep}"
		requestNovelsLib.getSqlite3DBFileToFiles(dbPath, targetPath, None, ispsDict, hspsDict)
	else:
		print("\n====\n\t没有发现 -db 选项\n====\n")
	getArgs = argvDict.get("adb")
	if getArgs is not None:
		print("\n====\n\t小说信息写入 txt 文件\n====\n")
		currentPath = requestNovelsLib.getCallAbsDirPath() + os.sep
		## 解压文件
		dbPath = f"{currentPath}out{os.sep}db{os.sep}"
		targetPath = f"{currentPath}out{os.sep}txt{os.sep}"
		try:
			requestNovelsLib.extrusionDB(dbPath, targetPath, None, ispsDict, hspsDict)
		except:
			msg = requestNovelsLib.getSysExcInfoErrorMsg()
			requestNovelsLib.__stdError(msg)
			requestNovelsLib.writeLogFile(msg, "extrusionDB erros")
	else:
		print("\n====\n\t没有发现 -adb 选项\n====\n")
	afOption = argvDict.get("af")
	if afOption is not None and len(afOption) > 0:
		fOption = argvDict.get("f")
		if fOption is None:
			fOption = []
			argvDict["f"] = fOption
		for path in afOption:
			afOptionFiles = requestNovelsLib.getPathFilesNames(path)
			for file in afOptionFiles:
				fOption.append(file)
	isFind = False
	kOption = argvDict.get("k")
	if kOption is not None and len(kOption) < 1:
		fOption = argvDict.get("f")
		if fOption is not None and len(fOption) > 0:
			isFind = True
	else:
		isFind = True
	if not isFind:
		print("\n====\n\t没有发现需要查找的项目\n====\n")
	else:
		print("\n====\n\t开始查找。并且录入 txt 文件\n====\n")
		## 获取所有查找的数据库
		getRequestFiles = getTargetAbsFilePath("out", "db")
		workPathRequestFiles = getTargetAbsFilePath(requestNovelsLib.getCallAbsDirPath(), "out/db")
		for workFileDb in workPathRequestFiles:
			getRequestFiles.append(workFileDb)
		workPathRequestFiles.clear()
		workPathRequestFiles = None
		if getRequestFiles is None or len(getRequestFiles) == 0:
			print("\n====\n\t不存在任意数据库，请更新数据库\n====\n")
		else:
			keyFindResult = None
			## 文件映射到信息
			print("获取相关关键小说")
			keyFindMap: dict[str, list[str]] = None
			## 文件映射到查找关键字
			try:
				keyFindMap = initFindKeys(argvDict.get('k'), argvDict.get('f'), argvDict.get('a'))
			except:
				msg = requestNovelsLib.getSysExcInfoErrorMsg()
				requestNovelsLib.__stdError(msg)
				requestNovelsLib.writeLogFile(msg, "find option error(initFindKeys)")
				exit(-1)
			fileContenMap: dict[str, list[requestNovelsLib.NovelInfo]] = None
			try:
				## 开始查找
				fileContenMap = getFindKeyInfo(keyFindMap, getRequestFiles, currentTimeStr, ispsDict=ispsDict, hspsDict=hspsDict)
			except:
				msg = requestNovelsLib.getSysExcInfoErrorMsg()
				requestNovelsLib.__stdError(msg)
				requestNovelsLib.writeLogFile(msg, "find option error(getFindKeyInfo)")
				exit(-1)
			## 写入文件
			print("\n====\n\t开始写入文件\n====\n")
			try:
				writeFindResultNovels(fileContenMap)
			except:
				msg = requestNovelsLib.getSysExcInfoErrorMsg()
				requestNovelsLib.__stdError(msg)
				requestNovelsLib.writeLogFile(msg, "find option error (writeFindResultNovels)")
				exit(-1)
		removeFiles = expireFileTimeFile(datetime_now)
		for file in removeFiles:
			requestNovelsLib.removeFile(file)
		removeEmptyDirs("out/find")
